use crate::common::tm_rpc_get;
use http::Method;
use hyper::{header::HeaderValue, Body, Request, Response, StatusCode};
use jsonrpc_http_server::{hyper, RequestMiddlewareAction, Response as RpcResp};
use once_cell::sync::OnceCell;
use ruc::*;
use serde_json::json;

static UPSTREAM: OnceCell<(String, bool)> = OnceCell::new();

#[inline(always)]
pub fn init_upstream(upstream: &str, is_unix: bool) -> Result<()> {
    UPSTREAM
        .set((upstream.to_owned(), is_unix))
        .map_err(|e| eg!(@e))
}

pub fn request_middle(request: Request<Body>) -> RequestMiddlewareAction {
    if request.method() == Method::GET {
        match request.uri().path() {
            "/version" => handle_version(request),
            other => handle_tm(other, &request),
        }
    } else {
        request.into()
    }
}

fn handle_version(mut request: Request<Body>) -> RequestMiddlewareAction {
    let uri = request.uri_mut();
    *uri = "/net_version".parse().unwrap();
    let params_vec = match serde_json::to_vec(&json!({
        "jsonrpc": "2.0",
        "method": "net_version",
        "params": [],
        "id": "1"
    })) {
        Ok(v) => v,
        Err(e) => return RpcResp::internal_error(e.to_string()).into(),
    };

    let net_version_request = Request::builder()
        .method("POST")
        .uri(uri.clone())
        .header("Content-Type", "application/json")
        .header("Connection", "close")
        .body(Body::from(params_vec))
        .unwrap();

    net_version_request.into()
}

#[inline(always)]
fn handle_tm(path: &str, request: &Request<Body>) -> RequestMiddlewareAction {
    let params = request.uri().query().map(parse_params).unwrap_or_default();
    do_handle_tm(path, &params)
}

fn do_handle_tm(path: &str, params: &[&str]) -> RequestMiddlewareAction {
    let body = if let Some((upstream, is_unix)) = UPSTREAM.get().as_ref() {
        match tm_rpc_get(upstream, path, params, *is_unix) {
            Ok((status_code, resp)) => {
                if 200 == status_code {
                    Body::from(resp)
                } else {
                    return err_with_code(status_code, &String::from_utf8_lossy(&resp));
                }
            }
            Err(e) => {
                return err(&e.to_string());
            }
        }
    } else {
        return err("upstream has not been initialized");
    };

    let mut response = Response::new(body);
    response
        .headers_mut()
        .insert("content-type", HeaderValue::from_static("application/json"));

    RequestMiddlewareAction::from(response)
}

#[inline(always)]
fn err(msg: &str) -> RequestMiddlewareAction {
    err_with_code(500, msg)
}

#[inline(always)]
fn err_with_code(status_code: u32, msg: &str) -> RequestMiddlewareAction {
    let mut response =
        Response::new(Body::from(super::error::new_jsonrpc_error(msg).to_string()));
    response
        .headers_mut()
        .insert("content-type", HeaderValue::from_static("application/json"));
    *response.status_mut() =
        pnk!(StatusCode::from_u16(pnk!(u16::try_from(status_code))));

    RequestMiddlewareAction::from(response)
}

#[inline(always)]
fn parse_params(query: &str) -> Vec<&str> {
    query.split('&').collect()
}
